/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package argtrust.argsys;

import java.util.List;
import java.util.Map;
import java.util.LinkedHashMap;
import java.util.Collection;
import java.util.ArrayList;
import java.util.Set;


import aima.core.logic.fol.parsing.ast.Sentence;

import argtrust.logic.FOLFactory;
//import edu.cuny.argtrust.util.ListMultiValueCache;
import argtrust.logic.InferenceNode;
import argtrust.logic.FactInferenceNode;
import argtrust.logic.RuleInferenceNode;
import argtrust.logic.FOLNaturalDeduction;
import argtrust.logic.FOLKnowledgeBase;

import argtrust.util.MyListIterator;

/**
 * An argumentation system.
 * TODO: Add a caching system.
 * @author Yuqing Tang <ytang@cs.gc.cuny.edu>
 */
public class ArgumentationSystem {

    //Input:
    List<DefeatRule> _DefeatRuleList = new ArrayList<DefeatRule>();
//    ListMultiValueCache<Argument, ArguingNode> mCache;
    Map<InferenceNode, ArguingNode> mEndInferenceCache;
    FOLNaturalDeduction mDeductionSys;

    public ArgumentationSystem() {
    }

    public ArgumentationSystem(FOLKnowledgeBase kb) {
        mDeductionSys = new FOLNaturalDeduction();
        mDeductionSys.acceptKnowledgeBase(kb);
        mEndInferenceCache = new LinkedHashMap<InferenceNode, ArguingNode>();
    }

    public void acceptKnowledge(FOLKnowledgeBase kb) {
        mDeductionSys = new FOLNaturalDeduction();
        mDeductionSys.acceptKnowledgeBase(kb);
        mEndInferenceCache = new LinkedHashMap<InferenceNode, ArguingNode>();
    }

    public void acceptDefeatRules(List<DefeatRule> defeat_rule_list) {
        _DefeatRuleList = defeat_rule_list;
    }

    /**
     * Return the deduction system.
     * For the debug usage mainly.
     * @return
     */
    public FOLNaturalDeduction getDeductionSys() {
        return mDeductionSys;
    }

    public ArguingNode getArguingNode(Sentence s) {
        return getArgGraph_(s);
    }

    public ArguingNode getArguingNode(Argument arg) {
        return getArgGraph_(arg);
    }

    public ArguingNode getArguingNode(InferenceNode end_inf) {
        return getArgGraph_(end_inf);
    }

    public List<ArguingNode> getArguingNodeList(Sentence goal) {
        List<ArguingNode> arg_list = new java.util.ArrayList<ArguingNode>();
        collectArguingNodeList(arg_list, goal);
        if (arg_list.isEmpty()) {
            return null;
        } else {
            return arg_list;
        }
    }

    public List<ArguingNode> getArguingNodeList(List<Sentence> goal_list) {
        if (goal_list == null) {
            return null;
        }
        List<ArguingNode> arg_list = new java.util.ArrayList<ArguingNode>();
        for (Sentence goal : goal_list) {
            collectArguingNodeList(arg_list, goal);
        }
        return arg_list;
    }

    public void collectArguingNodeList(List<ArguingNode> arg_list, Sentence s) {
        InferenceNode inf = mDeductionSys.getInferenceNet(s);
        Set<Sentence> explored_set = new java.util.LinkedHashSet<Sentence>();
        while (inf != null) {
            ArguingNode arg_node = getArgGraph_(inf);
            arg_list.add(arg_node);
            explored_set.clear();
            inf = mDeductionSys.getNextInferenceNet(explored_set, inf);
        }

    }

    protected ArguingNode getArgGraph_(Sentence s) {
        InferenceNode end_inf = mDeductionSys.getInferenceNet(s);
        return getArgGraph_(end_inf);
    }

    protected ArguingNode getArgGraph_(Argument arg) {
        return getArgGraph_(arg.getEndInferenceNode());
    }

    protected ArguingNode getArgGraph_(InferenceNode end_inf) {
        if (end_inf == null) {
            return null;
        }

        ArguingNode arg_node = mEndInferenceCache.get(end_inf);
        if (arg_node != null) {
            return arg_node;
        }
//        arg_node.mark();
        Argument arg = new Argument(end_inf);
        arg_node = new ArguingNode(arg);
        mEndInferenceCache.put(end_inf, arg_node);

        expandEndInfDefeaterNode(arg_node, end_inf);

        return arg_node;
    }

    public Collection<ArguingNode> getAllNodes() {
        return mEndInferenceCache.values();
    }

    /**
     * Expand all possible defeats on sub_inf of type.
     * @param type
     * @param arg_node
     * @param sub_inf
     * @param counter_p
     */
    void expandDefeat(int type,
            ArguingNode arg_node,
            InferenceNode sub_inf,
            Sentence counter_p) {
        InferenceNode counter_inf =
                mDeductionSys.getInferenceNet(counter_p);
        Set<Sentence> explored_set = new java.util.LinkedHashSet<Sentence>();

        while (counter_inf != null) {
            //for debug
//            System.out.print("arg creatation/examing: ");
//            counter_inf.debug_print(System.out);

            ArguingNode counter_arg_node = getArgGraph_(counter_inf);
            Argument counter_arg = counter_arg_node.getArgument();

            Defeat defeat = DefeatFactory.defeat(type,
                    counter_arg,
                    arg_node.getArgument(),
                    counter_inf, sub_inf);
            arg_node.addDefeaterNode(counter_arg_node, defeat);

            explored_set.clear();
            counter_inf = mDeductionSys.getNextInferenceNet(explored_set, counter_inf);
        }

    }

    void expandEndInfDefeaterNode(ArguingNode arg_node,
            InferenceNode end_inf) {
        if (end_inf instanceof FactInferenceNode) {
            expandRebut(arg_node, (FactInferenceNode) end_inf);
        } else if (end_inf instanceof RuleInferenceNode) {
            RuleInferenceNode rule_inf = (RuleInferenceNode) end_inf;
            this.expandRebut(arg_node, rule_inf);
            this.expandInferenceUndercut(arg_node, rule_inf);
            List<InferenceNode> children_inf_list = rule_inf.getChildrenList();
            for (InferenceNode further_inf : children_inf_list) {
                expandDefeaterNode(arg_node, further_inf);
            }
        }

    }

    void expandDefeaterNode(ArguingNode arg_node, InferenceNode sub_inf) {
        if (sub_inf instanceof FactInferenceNode) {
            expandPremiseUndercut(arg_node, (FactInferenceNode) sub_inf);
        } else if (sub_inf instanceof RuleInferenceNode) {
            RuleInferenceNode rule_inf = (RuleInferenceNode) sub_inf;
            expandIntermediaUndercut(arg_node, rule_inf);
            expandInferenceUndercut(arg_node, rule_inf);
            List<InferenceNode> children_inf_list = rule_inf.getChildrenList();
            for (InferenceNode further_inf : children_inf_list) {
                expandDefeaterNode(arg_node, further_inf);
            }
        }

    }

    ////////////////////////////////////////////////////////
    void expandIntermediaUndercut(ArguingNode arg_node, RuleInferenceNode sub_inf) {
        Sentence counter_p = FOLFactory.not(sub_inf.getRuleInstance().getConclusion());
        expandDefeat(DefeatFactory.INTERMEDIA_UNDERCUT, arg_node, sub_inf, counter_p);
        expandCustomizedDefeat(DefeatFactory.INTERMEDIA_UNDERCUT,
                arg_node,
                sub_inf,
                sub_inf.getRuleInstance().getConclusion());


    }

    void expandRebut(ArguingNode arg_node, FactInferenceNode sub_inf) {
        Sentence counter_p = FOLFactory.not(sub_inf.getFactInstance());
        expandDefeat(DefeatFactory.REBUT, arg_node, sub_inf, counter_p);
        expandCustomizedDefeat(DefeatFactory.REBUT,
                arg_node,
                sub_inf,
                sub_inf.getFactInstance());
    }

    void expandRebut(ArguingNode arg_node, RuleInferenceNode sub_inf) {
        Sentence counter_p = FOLFactory.not(sub_inf.getRuleInstance().getConclusion());
        expandDefeat(DefeatFactory.REBUT, arg_node, sub_inf, counter_p);
        expandCustomizedDefeat(DefeatFactory.REBUT,
                arg_node,
                sub_inf,
                sub_inf.getRuleInstance().getConclusion());
    }

    void expandInferenceUndercut(ArguingNode arg_node, RuleInferenceNode sub_inf) {
        Sentence counter_p = FOLFactory.not(sub_inf.getRuleInstance().sentencize());
        expandDefeat(DefeatFactory.INFERENCE_UNDERCUT, arg_node, sub_inf, counter_p);
        expandCustomizedDefeat(DefeatFactory.INFERENCE_UNDERCUT,
                arg_node,
                sub_inf,
                sub_inf.getRuleInstance().sentencize());

    }

    void expandPremiseUndercut(ArguingNode arg_node, FactInferenceNode sub_inf) {
        Sentence counter_p = FOLFactory.not(sub_inf.getFactInstance());
        expandDefeat(DefeatFactory.PREMISE_UNDERCUT, arg_node, sub_inf, counter_p);
        expandCustomizedDefeat(DefeatFactory.PREMISE_UNDERCUT,
                arg_node,
                sub_inf,
                sub_inf.getFactInstance());
    }
    ///////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////

    /**
     * TODO: Cache defeatee -> defeater (arguingNode)
     * @param type
     * @param arg_node
     * @param sub_inf
     * @param defeatee
     */
    void expandCustomizedDefeat(
            int type,
            ArguingNode arg_node,
            InferenceNode sub_inf,
            Sentence defeatee) {
        MyListIterator<DefeatRule> iter = new MyListIterator<DefeatRule>(_DefeatRuleList);
        while (iter.hasNext()) {
            DefeatRule defeat_rule = iter.next();
            Sentence counter_p =
                    mDeductionSys.getDefeater(
                    defeatee,
                    defeat_rule.getDefeatee(),
                    defeat_rule.getDefeater());

            if (counter_p != null) {
                expandDefeat(type, arg_node, sub_inf, counter_p);
            }
        }

    }
    ///////////////////////////////////////////////////////////////
}
